package com.ai.networking.netty.decoder;

import com.ai.common.constant.AlgorithmConstant;
import com.ai.common.constant.PlcConstants;
import com.ai.common.constant.enumP.AlarmRunStatus;
import com.ai.common.constant.enumP.Plc4SteelStatus;
import com.ai.entity.network.L1ToIdentifySysMessage;
import com.ai.networking.netty.client.BootNettyNewLink;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * 解码
 * @Author jiaok
 * @Date 2023-06-20 11:44
 */
@Slf4j
public class L1ToSysMessageDecoder extends ByteToMessageDecoder {


    /**
     * 会发送信号的ip
     */
    private static final ArrayList<String>  signalIpList= new ArrayList<>(Arrays.asList("140.24.30.3",
            "140.24.30.4","140.24.30.5","140.24.2.4",
            "127.0.0.1","192.168.8.153","192.168.8.33"));
//    private static Integer STACK_FRONT_STEEL_SLAB_ID = 0;
//    private static Integer STACK_BACK_STEEL_SLAB_ID = 0;

    private static String FRONT_STATUS = "0";
    private static String BACK_STATUS = "0";

    private static final Long INTERVAL_TIME =30000L;

    /**
     * 解析信道数据
     * @param channelHandlerContext
     * @param byteBuf
     * @param out 解析之后的数据
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext,
                          ByteBuf byteBuf,
                          List<Object> out) throws Exception {

        InetSocketAddress ipSocket = (InetSocketAddress) channelHandlerContext.channel().remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
        String plc = convertIpToPLC(clientIp);


        Integer port = ipSocket.getPort();
        if (signalIpList.contains(clientIp)){

            int num = byteBuf.readableBytes();
            byte[] signalBytes = new byte[num];
            byteBuf.readBytes(signalBytes);
            String plcSignal = Arrays.toString(signalBytes);
//            if (!plc.equals(PlcConstants.PLC_4_CODE)){
                log.info("===========================   接收到{} , {} 发送的信号 {} ===========================   ",plc,clientIp,plcSignal);
//            }

            Integer telLen = (int) signalBytes[1];
            if (telLen != 46){
                // TODO: 2023-07-26 長度不足
                log.info("=========================== 长度不足46  ===========================");
                return;
            }

            //先处理plc4发送的信号
            if (clientIp.equals(PlcConstants.PLC_4_IP)){
                /**
                 * 先检测当前时间，与上次算法报警时间是否相隔30s
                 */
                if (System.currentTimeMillis() - BootNettyNewLink.timeMillisFlag > INTERVAL_TIME){
                    log.info("===========================  读取到plc4的操作信号  ===========================");
                    L1ToIdentifySysMessage l1ToIdentifySysMessage = getPlc4ToIdentifySysMessage(clientIp, signalBytes, plcSignal, telLen,port);
                    //将解析过之后的数据放置于管道中
                    out.add(l1ToIdentifySysMessage);
                }else {
                    L1ToIdentifySysMessage l1ToIdentifySysMessage=new L1ToIdentifySysMessage();
                    l1ToIdentifySysMessage.setHeartBeat(true);
                    out.add(l1ToIdentifySysMessage);
                }
                return;
            }

            byte[] telIdBytes = new byte[6];
            System.arraycopy(signalBytes,2,telIdBytes,0,6);
            Integer telId = Integer.valueOf(new String(telIdBytes));
            log.info("===========================  操作信号：{}  ===========================",telId);

            //判断是否为心跳电文
            if (telId.equals(AlgorithmConstant.PLC_TO_SYSTEM_HEARTBEAT)
                    || telId.equals(AlgorithmConstant.FURNACE_TO_SYSTEM_HEARTBEAT_30_4)
                    || telId.equals(AlgorithmConstant.FURNACE_TO_SYSTEM_HEARTBEAT_30_5)){

                if (!plc.equals(PlcConstants.PLC_4_CODE)){
                log.info("===========================   开始解析{} ,   {}  发送的心跳信号 开始解析 ===========================   ",plc,clientIp);}

                L1ToIdentifySysMessage l1ToIdentifyHeartBeatSysMessage = getToIdentifyHeartBeatSysMessage(clientIp, plc, port, signalBytes, plcSignal, telLen, telId);
                //将解析过之后的数据放置于管道中
                out.add(l1ToIdentifyHeartBeatSysMessage);
                return;
            }



//            if (!plc.equals(PlcConstants.PLC_4_CODE)){
            log.info(" ===========================  开始解析{} ,   {}  发送的操作信号 =========================== ",plc,clientIp);
//        }


            L1ToIdentifySysMessage l1ToIdentifySysMessage = getL1ToIdentifySysMessage(clientIp, plc, port, signalBytes, plcSignal, telLen, telId);
//        byteBuf.markReaderIndex();//数据解析结束，标记当前位置
            //将解析过之后的数据放置于管道中
            out.add(l1ToIdentifySysMessage);
        }



    }

    /**
     * 解析plc4发送来的报文-------叠钢和测距功能
     * @param clientIp plc4
     * @param signalBytes 原始字节数组
     * @param plcSignal 信号记录
     * @param telLen 信号长度
     * @return 解析后的报文对象
     */
    private static L1ToIdentifySysMessage getPlc4ToIdentifySysMessage(String clientIp, byte[] signalBytes, String plcSignal, Integer telLen,Integer port) {

//              第一个位置是否有钢  1有钢、0无钢
        Integer  frontStatus = (int) signalBytes[2];

//              第一个位置钢坯ID 0无钢坯ID、有值对应钢板ID
        byte[] frontNumIdBytes ={0,0, signalBytes[3], signalBytes[4]};
        int frontNumId = bytesToInt2(frontNumIdBytes, 0);

//              第二个位置是否有钢   1有钢、0无钢
        byte backStatusByte = signalBytes[5];
        Integer backStatus = (int) signalBytes[5];

//              第二个位置钢坯ID  0无钢坯ID、有值对应钢板ID
        byte[] backNumIdBytes ={0,0, signalBytes[6], signalBytes[7]};
        int backNumId = bytesToInt2(backNumIdBytes, 0);

//              预留位置
        byte[] spareBytes = new byte[36];
        System.arraycopy(signalBytes,8,spareBytes,0,36);
        String spare = new String(spareBytes);
//              心跳计数器
        byte[] counterBytes = {0,0, signalBytes[44], signalBytes[45]};
        int counter = bytesToInt2(counterBytes, 0);

        log.info("===========================  解析plc4信号完毕： 第一个位置是否有钢： {}，第一个位置钢坯ID: {}， 第二个位置是否有钢 :{}， 第二个位置钢坯ID {}，心跳计数器:{} ==========================="
            ,frontStatus,frontNumId,backStatus,backNumId,counter);

        L1ToIdentifySysMessage l1ToIdentifySysMessage = new L1ToIdentifySysMessage();
        l1ToIdentifySysMessage.setTelId(AlgorithmConstant.TRACK_STACKING_STEEL);
        l1ToIdentifySysMessage.setFrontStatus(frontStatus);
        l1ToIdentifySysMessage.setFrontNumId(frontNumId);
        l1ToIdentifySysMessage.setBackStatus(backStatus);
        l1ToIdentifySysMessage.setBackNumId(backNumId);
        l1ToIdentifySysMessage.setSpare(spare);
        l1ToIdentifySysMessage.setCounter(counter);
        l1ToIdentifySysMessage.setTelLen(telLen);
        l1ToIdentifySysMessage.setClientIp(clientIp);
        l1ToIdentifySysMessage.setPlcSignal(plcSignal);
        l1ToIdentifySysMessage.setPlanNo(String.valueOf(frontNumId));
        l1ToIdentifySysMessage.setSourcePort(port);
        //判断当前信号是否为心跳信号
//        if (frontNumId==STACK_FRONT_STEEL_SLAB_ID && backNumId== STACK_BACK_STEEL_SLAB_ID){
//            //后续信号与前序信号的板坯id值相同，说明为心跳信号，
//            log.info("===========================  后续信号与前序信号的板坯id值相同,视为心跳信号   ===========================");
//            l1ToIdentifySysMessage.setHeartBeat(true);
//        }else {
//            //与前序信号不同，则为操作信号
//            STACK_FRONT_STEEL_SLAB_ID=frontNumId;
//            STACK_BACK_STEEL_SLAB_ID=backNumId;
//            l1ToIdentifySysMessage.setHeartBeat(false);
//        }

        //两个位置都用拥有钢铁
        String front=String.valueOf(frontStatus);
        String back =String.valueOf(backStatus);
//        l1ToIdentifySysMessage.setState(
//                (front.equals(Plc4SteelStatus.EXIST_STEEL.getValue()) && back.equals(Plc4SteelStatus.EXIST_STEEL.getValue()))?1:2
//        );
        if (front.equals(Plc4SteelStatus.EXIST_STEEL.getValue()) && back.equals(Plc4SteelStatus.EXIST_STEEL.getValue())) {
            //前后两个位置都有钢铁，且内存中的前后状态为有钢状态，则说明当前状态为持续发送的信号，视为心跳
            l1ToIdentifySysMessage.setHeartBeat(
                    Objects.equals(FRONT_STATUS, Plc4SteelStatus.EXIST_STEEL.getValue())    &&
                    Objects.equals(BACK_STATUS, Plc4SteelStatus.EXIST_STEEL.getValue())
            );
            FRONT_STATUS=Plc4SteelStatus.EXIST_STEEL.getValue();
            BACK_STATUS=Plc4SteelStatus.EXIST_STEEL.getValue();
            l1ToIdentifySysMessage.setState(AlarmRunStatus.ALARM_RUNNING.getValue());
        }else {
            //钢板已经全部走出，则视为关闭信号，并修改内存值

            if (FRONT_STATUS.equals(Plc4SteelStatus.NOT_EXIST_STEEL.getValue()) || BACK_STATUS.equals(Plc4SteelStatus.NOT_EXIST_STEEL.getValue())){
                //此时状态值的内存已经存在无板胚的状态，则说明为心跳
                l1ToIdentifySysMessage.setHeartBeat(true);
            }
            FRONT_STATUS=Plc4SteelStatus.NOT_EXIST_STEEL.getValue();
            BACK_STATUS=Plc4SteelStatus.NOT_EXIST_STEEL.getValue();
            l1ToIdentifySysMessage.setState(AlarmRunStatus.ALARM_UN_RUNNING.getValue());
        }

        return l1ToIdentifySysMessage;
    }

    /**
     * 解析plc心跳报文
     * @param clientIp 客户端ip
     * @param plc plcCode
     * @param port 端口
     * @param signalBytes 信号字节数组
     * @param plcSignal 原始信号，用于记录信号日志
     * @param telLen 信号长度
     * @param telId 信号id标识
     * @return
     */
    private static L1ToIdentifySysMessage getToIdentifyHeartBeatSysMessage(String clientIp, String plc, Integer port, byte[] signalBytes, String plcSignal, Integer telLen, Integer telId) {
        byte[] counterBytesOriginal = new byte[2];
        System.arraycopy(signalBytes,8,counterBytesOriginal,0,2);
        byte[] counterBytes=new byte[]{0,0,counterBytesOriginal[0],counterBytesOriginal[1]};
        int counter = bytesToInt2(counterBytes, 0);

        byte[] heartBeatSpareBytes = new byte[36];
        System.arraycopy(signalBytes,10,heartBeatSpareBytes,0,36);
        String heartBeatSpare=new String(heartBeatSpareBytes);

        log.info("===========================  心跳冗余信息：{} .心跳计数：{} ===========================",heartBeatSpare,counter);


        if (!plc.equals(PlcConstants.PLC_4_CODE)){
        log.info(" =========================== 解析{},{} 发送的心跳信号 解析完毕  ===========================  ", plc, clientIp);}
        L1ToIdentifySysMessage l1ToIdentifyHeartBeatSysMessage = new L1ToIdentifySysMessage();
        l1ToIdentifyHeartBeatSysMessage.setTelId(telId);
        l1ToIdentifyHeartBeatSysMessage.setTelLen(telLen);
        l1ToIdentifyHeartBeatSysMessage.setCounter(counter);
        l1ToIdentifyHeartBeatSysMessage.setSpare(heartBeatSpare);
        l1ToIdentifyHeartBeatSysMessage.setHeartBeat(true);
        //客户端ip
        l1ToIdentifyHeartBeatSysMessage.setClientIp(clientIp);
        //客户端端口
        l1ToIdentifyHeartBeatSysMessage.setSourcePort(port);
        l1ToIdentifyHeartBeatSysMessage.setPlcSignal(plcSignal);
        log.info("=========================== 接收到心跳报文：{}  ===========================", JSONObject.toJSONString(l1ToIdentifyHeartBeatSysMessage));
        return l1ToIdentifyHeartBeatSysMessage;
    }

    /**
     * 解析plc操作信号报文
     * @param clientIp  客户端ip
     * @param plc plcCode
     * @param port 端口
     * @param signalBytes 信号字节数组
     * @param plcSignal 原始信号，用于记录信号日志
     * @param telLen 信号长度
     * @param telId 信号id标识
     * @return
     */
    private static L1ToIdentifySysMessage getL1ToIdentifySysMessage(String clientIp, String plc, Integer port, byte[] signalBytes, String plcSignal, Integer telLen, Integer telId) {
        //  版批号----- 心跳电文没有
        byte[] planNoBytes = new byte[12];
        System.arraycopy(signalBytes,8,planNoBytes,0,12);
        String planNo = new String(planNoBytes);
        log.info("=========================== 操作板坯号：{}  ===========================",planNo);

        String state="";
        String doorNum="";
        //不同的电文信号，解析为不同的字段
        if (telId.equals(AlgorithmConstant.A3_TRANSFER_MACHINE_AUTOMATIC_CONFIRMATION) ||
                telId.equals(AlgorithmConstant.TRACK_STACKING_STEEL)){
            //A3移载机自动确认 开始/关闭指令 或者  输入辊道叠钢 开始/关闭指令
            byte [] stateBytes= new byte[2];
            state = Integer.toString(signalBytes[21]);
            log.info("=========================== 信号类型:{}  ===========================",state);
        } else  {
            doorNum=Integer.toString(signalBytes[21]);
            log.info("=========================== 炉门编号：{}  ===========================",doorNum);
        }

        //冗余字段
        byte[] spareBytes = new byte[24];
        System.arraycopy(signalBytes,22,spareBytes,0,24);
        String spare = new String(spareBytes);

        log.info("===========================  操作信号冗余信息：{} ===========================",spare);

        log.info(" ===========================  解析{} ,   {}  发送的操作信号 结束  =========================== ", plc, clientIp);
        L1ToIdentifySysMessage l1ToIdentifySysMessage = new L1ToIdentifySysMessage();
        l1ToIdentifySysMessage.setTelLen(telLen);
        l1ToIdentifySysMessage.setTelId(telId);
        l1ToIdentifySysMessage.setPlanNo(planNo);
        l1ToIdentifySysMessage.setHeartBeat(false);
        if (!StringUtils.isBlank(state)){
            l1ToIdentifySysMessage.setState(Integer.valueOf(state));
        } else if (!StringUtils.isBlank(spare)) {
            l1ToIdentifySysMessage.setDoorNum(Integer.valueOf(doorNum));
        }
        l1ToIdentifySysMessage.setSpare(spare);
        //客户端ip
        l1ToIdentifySysMessage.setClientIp(clientIp);
        //客户端端口
        l1ToIdentifySysMessage.setSourcePort(port);
        //plc信號
        l1ToIdentifySysMessage.setPlcSignal(plcSignal);
        log.info("=========================== 信号操作报文：{}  ===========================", JSONObject.toJSONString(l1ToIdentifySysMessage));
        return l1ToIdentifySysMessage;
    }

    /**
     * 由信号源ip分辨出是哪个plc
     * @param ip 信号源ip
     * @return
     */
    public String convertIpToPLC(String ip){
//        ","140.24.30.4","140.24.30.5","140.24.2.4"
        if (ip.equals(PlcConstants.PLC_1_IP)){
            return PlcConstants.PLC_1_CODE;
        } else if (ip.equals(PlcConstants.PLC_2_IP)) {
            return PlcConstants.PLC_2_CODE;
        } else if (ip.equals(PlcConstants.PLC_3_IP)) {
            return PlcConstants.PLC_3_CODE;
        } else if (ip.equals(PlcConstants.PLC_4_IP)) {
            return PlcConstants.PLC_4_CODE;
        }else {
            return PlcConstants.PLC_1_CODE;
        }
    }


    /**
     * byte数组中取int数值，本方法适用于(低位在后，高位在前)的顺序
     * @param src
     * @param offset 从数组的第offset位开始
     * @return
     */
    public static int bytesToInt2(byte[] src, int offset) {
        int value;
        value = (int) ( ((src[offset] & 0xFF)<<24)
                |((src[offset+1] & 0xFF)<<16)
                |((src[offset+2] & 0xFF)<<8)
                |(src[offset+3] & 0xFF));
        return value;
    }

//    public static void main(String[] args) {
//        byte [] a =new byte[]{(byte)0,(byte)0,1,-81};
//        int i = bytesToInt2(a, 0);
//        System.out.println(i);
//    }

}
